/*
   Class Name:

      CMD2Model.

   Created by:

      Allen Sherrod (Programming Ace of www.UltimateGameProgramming.com).

   Description:

      This class will load and store a Quake 2 .md2 character model.
*/


#include "CMd2Model.h"


CMd2Model::CMd2Model()
{
   // Initialize all variables.
   TriangleList = 0;
   FrameList = 0;
   vertex = 0;
   animatedMesh = 0;
   normal = 0;
   texCoords = 0;
   currentframe = -1.0;

   oscalar = -2.0f;
   ptcoords = 0;
   pvertices = 0;

   MakeVBO();
   rendermode = 1;

   framesize = 0;
   tcbuffsize = 0;
}


CMd2Model::~CMd2Model()
{
	FlushVBO();
   Shutdown();
}


bool CMd2Model::LoadModel(const char *filename)
{
	
   // Release all previous data.
   Shutdown();
   FILE *fp = 0;                             // File pointer
   fopen_s(&fp,filename, "rb");               // Open the model's .md2 file.
  

   if(!fp) return false;                     // If there are any errors return false.

   fseek(fp, 0, SEEK_END);                   // We use this set to get the length of the file.
   int length = ftell(fp);                   // Store the length of the file.
   fseek(fp, 0, SEEK_SET);
   char *buffer = 0;                         // buffer will hold the whole file.
   buffer = new char[length + 1];            // Create the buffer to a size big enough for it.

   if(!buffer) return false;                 // Error checking.
   
   fread(buffer, sizeof(char), length, fp);  // Read in the whole file into the buffer.

   // Close the file because we do not need it anymore.  We loaded the data in.
   fclose(fp);

   // Now that we have the whole model file loaded into the buffer we will now take out
   // all the information we will need piece by piece.

   // We will extract the model header from the buffer data into our header object.  First
   // we will get a pointer to the data then save all of the information into our Header
   // object like so.

   Md2Header *pHeader = 0;                   // Pointer to the data.

   pHeader = (Md2Header*)buffer;             // Extract the data from the buffer.

   // Save the data into our Header object piece by piece.
   md2Header.frameSize = pHeader->frameSize;
   md2Header.id = pHeader->id;
   md2Header.numFrames = pHeader->numFrames;
   md2Header.numGLCommands = pHeader->numGLCommands;
   md2Header.numTexCoords = pHeader->numTexCoords;
   md2Header.numTriangles = pHeader->numTriangles;
   md2Header.numVertices = pHeader->numVertices;
   md2Header.offsetEndOfFile = pHeader->offsetEndOfFile;
   md2Header.offsetFrames = pHeader->offsetFrames;
   md2Header.offsetGLCommands = pHeader->offsetGLCommands;
   md2Header.offsetSkins = pHeader->offsetSkins;
   md2Header.offsetTexCoords = pHeader->offsetTexCoords;
   md2Header.offsetTriangles = pHeader->offsetTriangles;
   md2Header.skinHeight = pHeader->skinHeight;
   md2Header.skinWidth = pHeader->skinWidth;
   md2Header.totalSkins = pHeader->totalSkins;
   md2Header.version = pHeader->version;

   // All .md2 files have a version number of 8.  Lets check to make sure this is true
   // because if it is not then this must not be a .md2 character model.
   if(md2Header.version != 8)
      {
         // Delete the data in the buffer.
         if(buffer != 0)
            {
               delete[] buffer;
               buffer = 0;
            }

         Shutdown();
         
         return false;
      }


   // Next lets read in the texture coordinate of the model.  Since we know
   // how many coords there are lets allocate enough space for it.
   texCoords = new CTexCoord[md2Header.numTexCoords];

   // Again like with extracting the header we will need a pointer to get the
   // data out of the buffer.
   stIndex *pTexList;

   // offsetTexCoords is a value in bytes that holds where the start of the
   // texture coordinates are located in the .md2 file.  Lets move to that place
   // in the buffer so we can read them in.
   pTexList = (stIndex*)&buffer[md2Header.offsetTexCoords];

   // Now lets store the texture coordinates in the texCoords object.
   for(int i = 0; i < md2Header.numTexCoords; i++)
      {
         texCoords[i].tu = (float)pTexList[i].s;
         texCoords[i].tv = (float)pTexList[i].t;
      }



   // Next we load the triangles..
   TriangleList = new stTriangleIndice[md2Header.numTriangles];

   // Next extract the data by moving to the spot in the data where the triangle data start.
   stTriangleIndice *pTriList;

   // Get the data.
   pTriList = (stTriangleIndice*)&buffer[md2Header.offsetTriangles];

   // Now we pretty much store the data in our TriangleList object.
   for(int i = 0; i < md2Header.numTriangles; i++)
      {
         TriangleList[i].triIndex[0] = pTriList[i].triIndex[0];
         TriangleList[i].triIndex[1] = pTriList[i].triIndex[1];
         TriangleList[i].triIndex[2] = pTriList[i].triIndex[2];
         TriangleList[i].texCoordIndex[0] = pTriList[i].texCoordIndex[0];
         TriangleList[i].texCoordIndex[1] = pTriList[i].texCoordIndex[1];
         TriangleList[i].texCoordIndex[2] = pTriList[i].texCoordIndex[2];
      }


   // Last we will load in all the frames of animation from the model data.
   // Like before we extract the data like so...
   stFrame *SingleFrame;

   // Allocate the data.
   vertex = new CVector4[md2Header.numVertices * md2Header.numFrames];

   // Loop through each frame extracting the data and storing it in the correct position
   // in the vertex object.
   for(int i = 0; i < md2Header.numFrames; i++)
      {
         SingleFrame = (stFrame*)&buffer[md2Header.offsetFrames + md2Header.frameSize * i];

         for(int j = 0; j < md2Header.numVertices; j++)
            {
               // Below we you notice that we swap the y and z.  This is to flip the
               // model so it is standing up instead of looking like its lying down.
               vertex[md2Header.numVertices * i + j].x = SingleFrame->scale[0] *
                                          SingleFrame->frameInfo[j].vertex[0] +
                                          SingleFrame->translate[0];

               vertex[md2Header.numVertices * i + j].y = SingleFrame->scale[2] *
                                          SingleFrame->frameInfo[j].vertex[2] +
                                          SingleFrame->translate[2];

               vertex[md2Header.numVertices * i + j].z = SingleFrame->scale[1] *
                                          SingleFrame->frameInfo[j].vertex[1] +
                                          SingleFrame->translate[1];
            }
      }


   // Allocate enough normals for a single mesh of a single frame.  This is because
   // UpdateModel will interpolate the model between two keyframe meshes.
   // We will calculate the normals as we interpolate.
   normal = new CVector4[md2Header.numTriangles];


   // Next we create a mesh that will be used to store the interpolated values
   // of two frames.  This is what we will draw to the screen.
   animatedMesh = new CVector4[md2Header.numTriangles * 3];

   // Delete the data in the buffer.
   if(buffer != 0)
      {
         delete[] buffer;
         buffer = 0;
      }

   // And we are done loading.  Return true and lets move on to rendering.
   return true;
}


void CMd2Model::AdjustTexCoords(int w, int h)
{
   // Adjust based on texture size.
   for(int i = 0; i < md2Header.numTexCoords; i++)
      {
         texCoords[i].tu /= (float)w;
         texCoords[i].tv /= (float)h;
      }
}

void CMd2Model::AutoAdjustTexCoords()
{
	float maxu=0,maxv=0;

	for(int i = 0; i < md2Header.numTexCoords; i++)
	{
		maxu = max_(maxu,texCoords[i].tu);
		maxv = max_(maxv,texCoords[i].tv);
	}

	maxu = (float)md2Header.skinWidth;
	maxv = (float)md2Header.skinHeight;

	AdjustTexCoords((int)maxu,(int)maxv);
}

void CMd2Model::UpdateModel(int currentFrameIndex,int nextFrameIndex,float scalar)
{
/*
		debug::Log<<"currentFrameIndex:"<<currentFrameIndex<<std::endl;
		debug::Log<<"nextFrameIndex:"<<nextFrameIndex<<std::endl;
		debug::Log<<"scalar:"<<scalar<<std::endl;*/
   // Data used for interpolation.
   CVector4 v1, v2;

   // Hold a temporary normal.
   CVector4 tempV;


   // thisFrame and nextFrame are pointers to the current mesh in the desired
   // animation and the mesh that will be in the frame after it.  We use these
   // to get the triangles of each frame and interpolate them by a scalar.
   // This will give us our animation.  A scalar of 0 means the first frame
   // is visible, 1 means the next frame is visible.  Anything in between will
   // give us a mesh that is between the two frames.  That new mesh is what we
   // are calculating below.
   CVector4 *thisFrame;
   CVector4 *nextFrame;

   // Get pointers the mesh of this frame and the mesh of next frame.
   thisFrame = &vertex[md2Header.numVertices * currentFrameIndex];
   nextFrame = &vertex[md2Header.numVertices * nextFrameIndex];

   // Loop through and interpolate the two meshes, saving the results in animationMesh.
   for(int i = 0, k = 0; i < md2Header.numTriangles; i++, k += 3)
      {
         // Save a copy of this and next frames first triangle.  Then we interpolate
         // the vectors using a simple interpolation equation.
         v1 = thisFrame[TriangleList[i].triIndex[0]];
         v2 = nextFrame[TriangleList[i].triIndex[0]];
         animatedMesh[k + 0] = v1 + (v2 - v1) * scalar;

         // Save a copy of this and next frames second triangle.
         v1 = thisFrame[TriangleList[i].triIndex[1]];
         v2 = nextFrame[TriangleList[i].triIndex[1]];
         animatedMesh[k + 1] = v1 + (v2 - v1) * scalar;

         // Save a copy of this and next frames third triangle.
         v1 = thisFrame[TriangleList[i].triIndex[2]];
         v2 = nextFrame[TriangleList[i].triIndex[2]];
         animatedMesh[k + 2] = v1 + (v2 - v1) * scalar;

         // Since we interpolated the data, we must calculate the normals
         // for each triangle so we have accurate values.
         //normal[i].Normalize(animatedMesh[k], animatedMesh[k + 1], animatedMesh[k + 2]);
      }
}


void CMd2Model::Shutdown()
{
   // Delete and clear everything our model used...
   if(TriangleList != 0)
      {
         delete[] TriangleList;
         TriangleList = 0;
      }

   if(FrameList != 0)
      {
         delete[] FrameList;
         FrameList = 0;
      }

   if(normal != 0)
      {
         delete[] normal;
         normal = 0;
      }

   if(animatedMesh != 0)
      {
         delete[] animatedMesh;
         animatedMesh = 0;
      }

   if(vertex != 0)
      {
         delete[] vertex;
         vertex = 0;
      }

   if(texCoords != 0)
      {
         delete[] texCoords;
         texCoords = NULL;
      }

   if(ptcoords !=0 )
   {
	   delete[] ptcoords;
	   ptcoords = 0;
   }

   if(pvertices != 0)
   {
	   delete[] pvertices;
	   pvertices = 0;
   }
}

void CMd2Model::Draw(int currentFrameIndex,int nextFrameIndex,float scalar)
{
	if(currentFrameIndex<0)currentFrameIndex=0;
	if(currentFrameIndex>md2Header.numFrames)currentFrameIndex=md2Header.numFrames;

	if(nextFrameIndex<0)nextFrameIndex=0;
	if(nextFrameIndex>md2Header.numFrames)nextFrameIndex=md2Header.numFrames;

	if(scalar<0)scalar = 0;
	if(scalar>1)scalar = 1;

	sys::renderedtris += md2Header.numTriangles;
	sys::renderedverts += md2Header.numVertices;

	if(rendermode==1)
	{
		//UpdateModel(currentFrameIndex,nextFrameIndex,scalar);

		CVector4 v1, v2;

		// are calculating below.
		CVector4 *thisFrame;
		CVector4 *nextFrame;

		// Get pointers the mesh of this frame and the mesh of next frame.
		thisFrame = &vertex[md2Header.numVertices * currentFrameIndex];
		nextFrame = &vertex[md2Header.numVertices * nextFrameIndex];

		glBindBuffer(GL_ARRAY_BUFFER,SVB);
		Vector3D *ptr = (Vector3D*)glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY);

		int ci=0;
		for(int i = 0; i < md2Header.numTriangles; i++)
		{

			v1 = thisFrame[TriangleList[i].triIndex[0]];
			v2 = nextFrame[TriangleList[i].triIndex[0]];
			v1.Interpolate(v2,scalar);

			ptr[ci].x = v1.x;
			ptr[ci].y = v1.y;
			ptr[ci].z = v1.z;
			++ci;

				// Save a copy of this and next frames second triangle.
			v1 = thisFrame[TriangleList[i].triIndex[1]];
			v2 = nextFrame[TriangleList[i].triIndex[1]];
			v1.Interpolate(v2,scalar);

			ptr[ci].x = v1.x;
			ptr[ci].y = v1.y;
			ptr[ci].z = v1.z;
			++ci;
	            
			// Save a copy of this and next frames third triangle.
			v1 = thisFrame[TriangleList[i].triIndex[2]];
			v2 = nextFrame[TriangleList[i].triIndex[2]];
			v1.Interpolate(v2,scalar);

			ptr[ci].x = v1.x;
			ptr[ci].y = v1.y;
			ptr[ci].z = v1.z;
			++ci;
		}

		glUnmapBuffer(GL_ARRAY_BUFFER);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,SIB);		
		Entts::last_indexB=SIB;

		glBindBuffer(GL_ARRAY_BUFFER_ARB, STB);
		glTexCoordPointer(2,GL_FLOAT,0,0);

		glBindBuffer(GL_ARRAY_BUFFER_ARB, SVB);
		glVertexPointer(3,GL_FLOAT,0,0);

		int cnti = md2Header.numTriangles * 3;

		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glEnableClientState(GL_VERTEX_ARRAY);

		glDrawRangeElements(GL_TRIANGLES,0,cnti-1,cnti,GL_UNSIGNED_SHORT, 0);

		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL_VERTEX_ARRAY);

	}

	if(rendermode==2)
	{


		if(Entts::last_indexB!=IBuff)
		{
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,IBuff);		
			Entts::last_indexB=IBuff;
		}

		if(Entts::last_vertexB!=VBuff)
		{
			glBindBuffer(GL_ARRAY_BUFFER_ARB, VBuff );
			Entts::last_vertexB=VBuff;

//			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//			glEnableClientState(GL_VERTEX_ARRAY);
		}

		glTexCoordPointer(2,GL_FLOAT,0,0);
		glVertexPointer(3,GL_FLOAT,0,(void*)(__int64)(tcbuffsize + framesize*currentFrameIndex));

		glDrawRangeElements(GL_TRIANGLES,0,cnti-1,cnti,GL_UNSIGNED_SHORT, 0);
	}

	if(rendermode==3)
	{
		if(sgl_shaders::current!=0)
		{
			glUniform1f(scalarloc,scalar);
		}
		else
		{
			//lerpsh->Bind();
			//glUniform1fARB(scalarloc,scalar);
		}

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,IBuff);
//		glIndexPointer(GL_UNSIGNED_SHORT,0,0);

//		glBindBufferARB(GL_ARRAY_BUFFER_ARB, TBuff );


		glEnableVertexAttribArray(attribloc);//new !!!

		glBindBuffer(GL_ARRAY_BUFFER, VBuff);//new !!!

		glTexCoordPointer(2,GL_FLOAT,0,0);

		glVertexAttribPointer(attribloc, 3, GL_FLOAT, GL_FALSE, 0, (void*)(__int64)(tcbuffsize + framesize*nextFrameIndex));//new !!!


		glVertexPointer(3,GL_FLOAT,0,(void*)(__int64)(tcbuffsize + framesize*currentFrameIndex));

		glDrawRangeElements(GL_TRIANGLES,0,cnti-1,cnti,GL_UNSIGNED_SHORT, 0);

		glDisableVertexAttribArray(attribloc);
	}

}

CMd2Model::CMd2Model(const char *filename, Shader* modifyshader = 0)
{
	
	TriangleList = 0;
	FrameList = 0;
	vertex = 0;
	animatedMesh = 0;
	normal = 0;
	texCoords = 0;
	currentframe = -1.0;

	oscalar = -2.0f;
	ptcoords = 0;
	pvertices = 0;

	framesize = 0;
	tcbuffsize = 0;

	SIB = 0;
	STB = 0;
	SVB = 0;

	LoadModel(filename);
	
	AutoAdjustTexCoords();
	

	geomsize = 0;

	for(int j=0;j<md2Header.numFrames;++j)
	{
		CVector4 *frame = &vertex[md2Header.numVertices * j];
		for(int i=0;i<md2Header.numVertices;++i)
		{
			CVector4 pos = frame[i];
			geomsize = max_(geomsize,pos.GetLength());
		}
	}

	MakeVBO();
	rendermode = 1;

	lerpsh = modifyshader;
	if(lerpsh)
	{
		scalarloc = lerpsh->GetUniformLocation("scalar");
		attribloc = lerpsh->GetAttribLocation("rm_NextVert");
	}

}

int CMd2Model::GetTotalFrames()
{
	return md2Header.numFrames;
}
typedef CMd2Model Md2Model;


float CMd2Model::GetSize()
{
	return geomsize;
}

void CMd2Model::MakeVBO()
{
	unsigned short int *newind = new unsigned short int [md2Header.numTriangles * 3];

	VBuff = 0;

	int ci=0;
	for(int i=0;i<md2Header.numTriangles;++i)
	{
		//for(int j=0;j<3;++j)
		//{
		//	newind[i*3+j] = i*3+j;
		//}
		newind[ci++] = TriangleList[i].texCoordIndex[0];
		newind[ci++] = TriangleList[i].texCoordIndex[1];
		newind[ci++] = TriangleList[i].texCoordIndex[2];
	}

	cnti = md2Header.numTriangles*3;
	OptimizeIndexes(0,24,cnti,newind);
	ci=0;

	glGenBuffers(1,&IBuff);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,IBuff); 
	glBufferData( GL_ELEMENT_ARRAY_BUFFER_ARB,sizeof(unsigned short int)*cnti,newind,GL_STATIC_DRAW_ARB);


	glGenBuffers(1,&VBuff);		

	tcbuffsize = md2Header.numTexCoords * 2 * sizeof(float);
	framesize = md2Header.numTexCoords*sizeof(float)*3;

	unsigned int buffsize = tcbuffsize + framesize* md2Header.numFrames;

	float *buffdata = new float[buffsize/sizeof(float)];

	for(int i=0;i<md2Header.numTexCoords;++i)
	{
		buffdata[i*2] = texCoords[i].tu;
		buffdata[i*2+1] = texCoords[i].tv;
	}

	for(int f=0;f<md2Header.numFrames;++f)
	{
		ci=0;
		CVector4 *frame = &vertex[md2Header.numVertices * f];
		for(int i=0;i<md2Header.numTexCoords;++i)
		{
			int ind = 0;
			for(int j=0;j<md2Header.numTriangles;++j)
			{
				for(int k=0;k<3;++k)
				{
					if(i == TriangleList[j].texCoordIndex[k])
					{
						ind = TriangleList[j].triIndex[k];
					}
				}
			}
			
			int index = (tcbuffsize + f*framesize) / sizeof(float) + ci;
			buffdata[index+0] = frame[ind].x;
			buffdata[index+1] = frame[ind].y;
			buffdata[index+2] = frame[ind].z;

			ci+=3;
		}
		
	}

	glBindBuffer(GL_ARRAY_BUFFER_ARB,VBuff);
	glBufferData(GL_ARRAY_BUFFER_ARB,buffsize,buffdata, GL_STATIC_DRAW_ARB );
	debug::Log<<"buffsize:"<<buffsize<<std::endl;

	delete[] buffdata;

	delete[] newind;



	//Create Stream VBOs

	int cnti = md2Header.numTriangles*3;

	unsigned short int *scalarindices = new unsigned short int[cnti];

	for(int i=0;i<cnti;++i)
	{
		scalarindices[i] = i;
	}

	glGenBuffers(1,&SIB);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,SIB); 
	glBufferData( GL_ELEMENT_ARRAY_BUFFER_ARB,sizeof(unsigned short int)*cnti
		,scalarindices,GL_STATIC_DRAW_ARB);

	Vector3D *verts = new Vector3D[cnti];

	for(int i=0;i<cnti;++i)
	{
		verts[i] = Vector3D(0,0,0);
	}

	Vector2D *uvs = new Vector2D[cnti];

	for(int i=0;i<md2Header.numTriangles;++i)
	{
		for(int j=0;j<3;++j)
		{
			uvs[i*3 + j].x = texCoords[TriangleList[i].texCoordIndex[j]].tu;
			uvs[i*3 + j].y = texCoords[TriangleList[i].texCoordIndex[j]].tv;
		}
	}

	glGenBuffers(1,&STB);
	glBindBuffer(GL_ARRAY_BUFFER,STB);
	glBufferData(GL_ARRAY_BUFFER,sizeof(Vector2D)*cnti,uvs,GL_STATIC_DRAW);

	glGenBuffers(1,&SVB);
	glBindBuffer(GL_ARRAY_BUFFER,SVB);	
	glBufferData(GL_ARRAY_BUFFER,sizeof(Vector3D)*cnti,verts,GL_STREAM_DRAW);


	
	delete[] scalarindices;
	delete[] verts;
	delete[] uvs;

}

void CMd2Model::FlushVBO()
{

	if(IBuff!=0){glDeleteBuffers(1,&IBuff);}
//	if(TBuff!=0){glDeleteBuffersARB(1,&TBuff);}

	if(VBuff!=0){glDeleteBuffers(1,&VBuff);}

	if(SIB!=0)
		glDeleteBuffers(1,&SIB);

	if(STB!=0)
		glDeleteBuffers(1,&STB);

	if(SVB!=0)
		glDeleteBuffers(1,&SVB);


}

void CMd2Model::SetRenderMode(int mode)
{
	if(sgl_shaders::version<1 && mode == 3)
		mode = 1;

	rendermode = mode;
}

int CMd2Model::GetCountTris()
{
	return md2Header.numTriangles;
}

int CMd2Model::GetCountVerts()
{
	return md2Header.numVertices;
}

int CMd2Model::GetCountTexCoords()
{
	return md2Header.numTexCoords;
}